﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Deferred.Editor.Content;
using Deferred.Library;
using Deferred.Library.Scene;

namespace Deferred.Editor.Gui.Scene
{
    public partial class ScenePanel : UserControl
    {
        private ContentUpdater sceneUpdater;
        private SceneGraph scene;
        private DrawableObjectAddedEvent drawableObjectAddedHandler;
        private TerrainAddedEvent terrainAddedHandler;
        private DirectionalLightAddedEvent directionalLightAddedHandler;
        private PointLightAddedEvent pointLightAddedHandler;
        private SpotLightAddedEvent spotLightAddedHandler;
        private AmbientLightAddedEvent ambientLightAddedHandler;
        private DrawableObjectDeletedEvent drawableObjectDeletedHandler;
        private TerrainDeletedEvent terrainDeletedHandler;
        private DirectionalLightDeletedEvent directionalLightDeletedHandler;
        private PointLightDeletedEvent pointLightDeletedHandler;
        private SpotLightDeletedEvent spotLightDeletedHandler;
        private AmbientLightDeletedEvent ambientLightDeletedHandler;
        private EventHandler cameraChangedHandler;
        private DeferredBase deferredBase;
        private int objectCounter = 1;
        private int terrainCounter = 1;
        private int directionalLightCounter = 1;
        private int pointLightCounter = 1;
        private int spotLightCounter = 1;
        private int ambientLightCounter = 1;
        private Dictionary<DrawableObject, TreeNode> objectToNode = new Dictionary<DrawableObject, TreeNode>();
        private Dictionary<Terrain, TreeNode> terrainToNode = new Dictionary<Terrain, TreeNode>();
        private Dictionary<DirectionalLight, TreeNode> directionalToNode = new Dictionary<DirectionalLight, TreeNode>();
        private Dictionary<PointLight, TreeNode> pointToNode = new Dictionary<PointLight, TreeNode>();
        private Dictionary<SpotLight, TreeNode> spotToNode = new Dictionary<SpotLight, TreeNode>();
        private Dictionary<AmbientLight, TreeNode> ambientToNode = new Dictionary<AmbientLight, TreeNode>();
        private ContentChooserWindow chooserWindow;

        public ScenePanel(DeferredBase deferredBase, ContentUpdater sceneUpdater, ContentChooserWindow chooserWindow)
        {
            InitializeComponent();

            this.chooserWindow = chooserWindow;

            this.deferredBase = deferredBase;
            this.sceneUpdater = sceneUpdater;
            sceneUpdater.CurrentSceneChanged += new EventHandler(delegate(object s, EventArgs e) { updateScene(); });

            drawableObjectAddedHandler = new DrawableObjectAddedEvent(AddObject);
            terrainAddedHandler = new TerrainAddedEvent(AddTerrain);
            directionalLightAddedHandler = new DirectionalLightAddedEvent(AddDirectionalLight);
            pointLightAddedHandler = new PointLightAddedEvent(AddPointLight);
            spotLightAddedHandler = new SpotLightAddedEvent(AddSpotLight);
            ambientLightAddedHandler = new AmbientLightAddedEvent(AddAmbientLight);
            drawableObjectDeletedHandler = new DrawableObjectDeletedEvent(DeleteObject);
            terrainDeletedHandler = new TerrainDeletedEvent(DeleteTerrain);
            directionalLightDeletedHandler = new DirectionalLightDeletedEvent(DeleteDirectionalLight);
            pointLightDeletedHandler = new PointLightDeletedEvent(DeletePointLight);
            spotLightDeletedHandler = new SpotLightDeletedEvent(DeleteSpotLight);
            ambientLightDeletedHandler = new AmbientLightDeletedEvent(DeleteAmbientLight);
            cameraChangedHandler = new EventHandler(delegate(object sender, EventArgs e) { CameraChanged(); });
        }

        private void updateScene()
        {
            if (scene != null)
            {
                scene.DrawableObjectAdded -= drawableObjectAddedHandler;
                scene.TerrainAdded -= terrainAddedHandler;
                scene.DirectionalLightAdded -= directionalLightAddedHandler;
                scene.PointLightAdded -= pointLightAddedHandler;
                scene.SpotLightAdded -= spotLightAddedHandler;
                scene.AmbientLightAdded -= ambientLightAddedHandler;
                scene.DrawableObjectDeleted -= drawableObjectDeletedHandler;
                scene.TerrainDeleted -= terrainDeletedHandler;
                scene.DirectionalLightDeleted -= directionalLightDeletedHandler;
                scene.PointLightDeleted -= pointLightDeletedHandler;
                scene.SpotLightDeleted -= spotLightDeletedHandler;
                scene.AmbientLightDeleted -= ambientLightDeletedHandler;
                scene.CameraChanged -= cameraChangedHandler;
            }

            scene = sceneUpdater.CurrentScene;

            sceneTree.Nodes["lightRoot"].Nodes["ambientRoot"].Nodes.Clear();
            sceneTree.Nodes["lightRoot"].Nodes["spotRoot"].Nodes.Clear();
            sceneTree.Nodes["lightRoot"].Nodes["pointRoot"].Nodes.Clear();
            sceneTree.Nodes["lightRoot"].Nodes["directionalRoot"].Nodes.Clear();
            sceneTree.Nodes["objectRoot"].Nodes.Clear();
            sceneTree.Nodes["terrainRoot"].Nodes.Clear();
            sceneTree.Nodes.RemoveByKey("cameraRoot");

            objectCounter = 1;
            terrainCounter = 1;
            directionalLightCounter = 1;
            pointLightCounter = 1;
            spotLightCounter = 1;
            ambientLightCounter = 1;

            objectToNode.Clear();
            terrainToNode.Clear();
            directionalToNode.Clear();
            pointToNode.Clear();
            spotToNode.Clear();
            ambientToNode.Clear();

            if (scene != null)
            {
                foreach (DrawableObject o in scene.DrawableObjects)
                {
                    AddObject(o);
                }
                foreach (Terrain t in scene.Terrains)
                {
                    AddTerrain(t);
                }
                foreach (DirectionalLight light in scene.DirectionalLights)
                {
                    AddDirectionalLight(light);
                }
                foreach (PointLight light in scene.PointLights)
                {
                    AddPointLight(light);
                }
                foreach (SpotLight light in scene.SpotLights)
                {
                    AddSpotLight(light);
                }
                foreach (AmbientLight light in scene.AmbientLights)
                {
                    AddAmbientLight(light);
                }

                AddCameraNode();

                scene.DrawableObjectAdded += drawableObjectAddedHandler;
                scene.TerrainAdded += terrainAddedHandler;
                scene.DirectionalLightAdded += directionalLightAddedHandler;
                scene.PointLightAdded += pointLightAddedHandler;
                scene.SpotLightAdded += spotLightAddedHandler;
                scene.AmbientLightAdded += ambientLightAddedHandler;
                scene.DrawableObjectDeleted += drawableObjectDeletedHandler;
                scene.TerrainDeleted += terrainDeletedHandler;
                scene.DirectionalLightDeleted += directionalLightDeletedHandler;
                scene.PointLightDeleted += pointLightDeletedHandler;
                scene.SpotLightDeleted += spotLightDeletedHandler;
                scene.AmbientLightDeleted += ambientLightDeletedHandler;
                scene.CameraChanged += cameraChangedHandler;
            }
        }

        private void AddAmbientLight(AmbientLight light)
        {
            TreeNode newNode = new TreeNode("AmbientLight" + ambientLightCounter);
            newNode.Tag = light;
            newNode.ContextMenuStrip = ambientLightMenuStrip;
            ambientLightCounter++;
            sceneTree.Nodes["lightRoot"].Nodes["ambientRoot"].Nodes.Add(newNode);
            ambientToNode.Add(light, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void AddSpotLight(SpotLight light)
        {
            TreeNode newNode = new TreeNode("SpotLight" + spotLightCounter);
            newNode.Tag = light;
            newNode.ContextMenuStrip = spotLightMenuStrip;
            spotLightCounter++;
            sceneTree.Nodes["lightRoot"].Nodes["spotRoot"].Nodes.Add(newNode);
            spotToNode.Add(light, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void AddPointLight(PointLight light)
        {
            TreeNode newNode = new TreeNode("PointLight" + pointLightCounter);
            newNode.Tag = light;
            newNode.ContextMenuStrip = pointLightMenuStrip;
            pointLightCounter++;
            sceneTree.Nodes["lightRoot"].Nodes["pointRoot"].Nodes.Add(newNode);
            pointToNode.Add(light, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void AddDirectionalLight(DirectionalLight light)
        {
            TreeNode newNode = new TreeNode("DirectionalLight" + directionalLightCounter);
            newNode.Tag = light;
            newNode.ContextMenuStrip = directionalLightMenuStrip;
            directionalLightCounter++;
            sceneTree.Nodes["lightRoot"].Nodes["directionalRoot"].Nodes.Add(newNode);
            directionalToNode.Add(light, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void AddObject(DrawableObject o)
        {
            TreeNode newNode = new TreeNode("DrawableObject" + objectCounter);
            newNode.Tag = o;
            newNode.ContextMenuStrip = objectMenuStrip;
            objectCounter++;
            sceneTree.Nodes["objectRoot"].Nodes.Add(newNode);
            objectToNode.Add(o, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void AddTerrain(Terrain t)
        {
            TreeNode newNode = new TreeNode("Terrain" + terrainCounter);
            newNode.Tag = t;
            newNode.ContextMenuStrip = terrainMenuStrip;
            terrainCounter++;
            sceneTree.Nodes["terrainRoot"].Nodes.Add(newNode);
            terrainToNode.Add(t, newNode);
            sceneTree.SelectedNode = newNode;
        }

        private void CameraChanged()
        {
            sceneTree.Nodes.RemoveByKey("cameraRoot");
            AddCameraNode();
        }

        private void AddCameraNode()
        {
            TreeNode newNode = sceneTree.Nodes.Add("cameraRoot", "Camera");
            newNode.Tag = scene.Camera;
        }

        private void DeleteAmbientLight(AmbientLight light)
        {
            sceneTree.Nodes["lightRoot"].Nodes["ambientRoot"].Nodes.Remove(ambientToNode[light]);
        }

        private void DeleteSpotLight(SpotLight light)
        {
            sceneTree.Nodes["lightRoot"].Nodes["spotRoot"].Nodes.Remove(spotToNode[light]);
        }

        private void DeletePointLight(PointLight light)
        {
            sceneTree.Nodes["lightRoot"].Nodes["pointRoot"].Nodes.Remove(pointToNode[light]);
        }

        private void DeleteDirectionalLight(DirectionalLight light)
        {
            sceneTree.Nodes["lightRoot"].Nodes["directionalRoot"].Nodes.Remove(directionalToNode[light]);
        }

        private void DeleteObject(DrawableObject o)
        {
            sceneTree.Nodes["objectRoot"].Nodes.Remove(objectToNode[o]);
        }

        private void DeleteTerrain(Terrain t)
        {
            sceneTree.Nodes["terrainRoot"].Nodes.Remove(terrainToNode[t]);
        }

        private void addObjectMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new DrawableObject());
        }

        private void addTerrainMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new Terrain());
        }

        private void addAmbientLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new AmbientLight());
        }

        private void addPointLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new PointLight());
        }

        private void addSpotLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new SpotLight());
        }

        private void addDirectionalLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Add(new DirectionalLight());
        }

        private void deleteAmbientLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((AmbientLight)sceneTree.SelectedNode.Tag);
        }

        private void deleteObjectMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((DrawableObject)sceneTree.SelectedNode.Tag);
        }

        private void deleteTerrainMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((Terrain)sceneTree.SelectedNode.Tag);
        }

        private void deletePointLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((PointLight)sceneTree.SelectedNode.Tag);
        }

        private void deleteSpotLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((SpotLight)sceneTree.SelectedNode.Tag);
        }

        private void deleteDirectionalLightMenuItem_Click(object sender, EventArgs e)
        {
            scene.Delete((DirectionalLight)sceneTree.SelectedNode.Tag);
        }

        private void sceneTree_MouseClick(object sender, MouseEventArgs e)
        {
            if (MouseButtons.Right == e.Button)
            {
                sceneTree.SelectedNode = sceneTree.GetNodeAt(e.X, e.Y);
            }
        }

        private void sceneTree_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (sceneTree.SelectedNode.Parent != null)
            {
                switch (sceneTree.SelectedNode.Parent.Name)
                {
                    case "ambientRoot":
                        AmbientLight ambientLight = (AmbientLight)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        AmbientLightPanel ambientPanel = new AmbientLightPanel(ambientLight);
                        ambientPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(ambientPanel);
                        break;
                    case "directionalRoot":
                        DirectionalLight directionalLight = (DirectionalLight)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        DirectionalLightPanel directionalPanel = new DirectionalLightPanel(directionalLight);
                        directionalPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(directionalPanel);
                        break;
                    case "pointRoot":
                        PointLight pointLight = (PointLight)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        PointLightPanel pointPanel = new PointLightPanel(pointLight);
                        pointPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(pointPanel);
                        break;
                    case "spotRoot":
                        SpotLight spotLight = (SpotLight)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        SpotLightPanel spotPanel = new SpotLightPanel(spotLight);
                        spotPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(spotPanel);
                        break;
                    case "objectRoot":
                        DrawableObject o = (DrawableObject)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        DrawableObjectPanel objectPanel = new DrawableObjectPanel(o, chooserWindow);
                        objectPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(objectPanel);
                        break;
                    case "terrainRoot":
                        Terrain t = (Terrain)sceneTree.SelectedNode.Tag;
                        if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                        TerrainPanel terrainPanel = new TerrainPanel(t, chooserWindow);
                        terrainPanel.Dock = DockStyle.Fill;
                        editPanel.Controls.Add(terrainPanel);
                        break;
                    default:
                        editPanel.Controls.Clear();
                        break;
                }
            }
            else if ("cameraRoot" == sceneTree.SelectedNode.Name)
            {
                Camera c = (Camera)sceneTree.SelectedNode.Tag;
                if (editPanel.Controls.Count > 0) editPanel.Controls.Clear();
                CameraPanel cameraPanel = new CameraPanel(c);
                cameraPanel.Dock = DockStyle.Fill;
                editPanel.Controls.Add(cameraPanel);
            }
        }

        private void sceneTree_KeyUp(object sender, KeyEventArgs e)
        {
            if (Keys.Delete == e.KeyCode || Keys.Back == e.KeyCode && null != sceneTree.SelectedNode.Parent)
            {
                switch (sceneTree.SelectedNode.Parent.Name)
                {
                    case "ambientRoot":
                        scene.Delete((AmbientLight)sceneTree.SelectedNode.Tag);
                        break;
                    case "directionalRoot":
                        scene.Delete((DirectionalLight)sceneTree.SelectedNode.Tag);
                        break;
                    case "pointRoot":
                        scene.Delete((PointLight)sceneTree.SelectedNode.Tag);
                        break;
                    case "spotRoot":
                        scene.Delete((SpotLight)sceneTree.SelectedNode.Tag);
                        break;
                    case "objectRoot":
                        scene.Delete((DrawableObject)sceneTree.SelectedNode.Tag);
                        break;
                    case "terrainRoot":
                        scene.Delete((Terrain)sceneTree.SelectedNode.Tag);
                        break;
                }
            }
        }
    }
}
